﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

namespace RawSocketDll
{
    [StructLayout(LayoutKind.Explicit)]
    public struct IPHeader
    {
        [FieldOffset(0)] public byte Verlen;
        [FieldOffset(1)] public byte Tos;
        [FieldOffset(2)] public ushort Totallength;
        [FieldOffset(4)] public ushort Id;
        [FieldOffset(6)] public ushort Offset;
        [FieldOffset(8)] public byte Ttl;
        [FieldOffset(9)] public byte Protocol;
        [FieldOffset(10)] public ushort Checksum;
        [FieldOffset(12)] public uint Srcaddr;
        [FieldOffset(16)] public uint Destaddr;
    }

    public class RawSocket
    {
        #region Delegates

        public delegate void PacketArrivedEventHandler(
            Object sender, PacketArrivedEventArgs args);

        #endregion

        private const int SIO_1 = unchecked((int) 0x98000002);

        private const int SIO_2 = unchecked((int) 0x98000003);

        private const int SIO_R = unchecked((int) 0x98000001);

        private readonly byte[] buff;

        private readonly SocketAsyncEventArgs socketEvent;

        public RawSocket(IPAddress ip):this(ip,4096)
        {

        }

        public RawSocket(IPAddress ip,int buffLen) 
        {
            BuffLen = buffLen;
            buff = new byte[BuffLen];
            socketEvent = new SocketAsyncEventArgs();
            socketEvent.Completed += SocketEventCompleted;
            socketEvent.SetBuffer(buff, 0, BuffLen);
            InnerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP) {Blocking = false};
            InnerSocket.Bind(new IPEndPoint(ip, 0));
            if (SetSocketOption() == false) ErrorOccurred = true;
        }

        public int BuffLen { get; set; }

        public bool KeepRunning { get; set; }

        public Socket InnerSocket { get; private set; }

        public bool ErrorOccurred { get; private set; }

        public void Shutdown()
        {
            if (InnerSocket != null)
            {
                InnerSocket.Shutdown(SocketShutdown.Both);
                InnerSocket.Close();
            }
        }

        private bool SetSocketOption()
        {
            var retValue = true;
            try
            {
                InnerSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1);
                var inBytes = new byte[4] {1, 0, 0, 0};
                var outBytes = new byte[4];
                InnerSocket.IOControl(SIO_R, inBytes, outBytes); //低级别操作模式
                var retCode = outBytes[0] + outBytes[1] + outBytes[2] + outBytes[3]; //把4个8位字节合成一个32位整数
                if (retCode != 0) retValue = false;
            }
            catch (SocketException)
            {
                retValue = false;
            }
            return retValue;
        }

        //解析接收的数据包，形成PacketArrivedEventArgs时间数据类对象，并引发PacketArrival事件
        private unsafe void Receive(SocketAsyncEventArgs e)
        {
            uint tempVersion = 0;
            IPAddress tempIP;

            var packetEvent = new PacketArrivedEventArgs(e);

            fixed (byte* fixedBuf = e.Buffer)
            {
                var head = (IPHeader*) fixedBuf;
                packetEvent.HeaderLength = (uint) (head->Verlen & 0x0F) << 2;

                switch (head->Protocol)
                {
                    case 1:
                        packetEvent.Protocol = "ICMP";
                        break;
                    case 2:
                        packetEvent.Protocol = "IGMP";
                        break;
                    case 6:
                        packetEvent.Protocol = "TCP";
                        break;
                    case 17:
                        packetEvent.Protocol = "UDP";
                        break;
                    default:
                        packetEvent.Protocol = "UNKNOWN";
                        break;
                }

                tempVersion = (uint) (head->Verlen & 0xF0) >> 4;
                packetEvent.IPVersion = tempVersion.ToString();

                var tempIPSrcaddr = head->Srcaddr;
                var tempIPDestaddr = head->Destaddr;
                tempIP = new IPAddress(tempIPSrcaddr);
                packetEvent.OriginationAddress = tempIP.ToString();
                tempIP = new IPAddress(tempIPDestaddr);
                packetEvent.DestinationAddress = tempIP.ToString();

                var tempSrcport = *(short*) &fixedBuf[packetEvent.HeaderLength];
                var tempDstport = *(short*) &fixedBuf[packetEvent.HeaderLength + 2];
                packetEvent.OriginationPort = IPAddress.NetworkToHostOrder(tempSrcport).ToString();
                packetEvent.DestinationPort = IPAddress.NetworkToHostOrder(tempDstport).ToString();
                packetEvent.PacketLength = (uint) e.BytesTransferred;
                packetEvent.MessageLength = (uint) e.BytesTransferred - packetEvent.HeaderLength;
            }
            //引发PacketArrival事件
            OnPacketArrival(packetEvent);
        }

        public void Run()
        {
            if (! InnerSocket.ReceiveAsync(socketEvent))
            {
                throw new Exception("receive packet failed.");
            }
        }

        private void SocketEventCompleted(object sender, SocketAsyncEventArgs e)
        {
            Receive(e);
            if (KeepRunning) Run();
        }

        public event PacketArrivedEventHandler PacketArrival;

        protected virtual void OnPacketArrival(PacketArrivedEventArgs e)
        {
            if (PacketArrival != null)
            {
                PacketArrival(this, e);
            }
        }

        #region Nested type: PacketArrivedEventArgs

        public class PacketArrivedEventArgs : EventArgs
        {
            public PacketArrivedEventArgs(SocketAsyncEventArgs args)
            {
                Protocol = string.Empty;
                DestinationPort = string.Empty;
                OriginationPort = string.Empty;
                DestinationAddress = string.Empty;
                OriginationAddress = string.Empty;
                IPVersion = string.Empty;

                PacketLength = 0;
                MessageLength = 0;
                HeaderLength = 0;

                ReceiveBuffer = new byte[args.BytesTransferred];
                Array.Copy(args.Buffer, ReceiveBuffer, args.BytesTransferred);
            }

            public string Protocol { get; set; }

            public string DestinationPort { get; set; }

            public string OriginationPort { get; set; }

            public string DestinationAddress { get; set; }

            public string OriginationAddress { get; set; }

            public string IPVersion { get; set; }

            public uint PacketLength { get; set; }

            public uint MessageLength { get; set; }

            public uint HeaderLength { get; set; }

            public byte[] ReceiveBuffer { get; set; }

            public byte[] IPHeaderBuffer { get; set; }

            public byte[] MessageBuffer { get; set; }
        }

        #endregion
    }
}